/***************************************************************************
 *   Copyright (c) 2008 Werner Mayer <wmayer[at]users.sourceforge.net>     *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development system.              *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Library General Public           *
 *   License as published by the Free Software Foundation; either          *
 *   version 2 of the License, or (at your option) any later version.      *
 *                                                                         *
 *   This library  is distributed in the hope that it will be useful,      *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU Library General Public License for more details.                  *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; see the file COPYING.LIB. If not,    *
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
 *   Suite 330, Boston, MA  02111-1307, USA                                *
 *                                                                         *
 ***************************************************************************/

#include "PreCompiled.h"
#ifndef _PreComp_
#ifdef FC_OS_WIN32
#include <Windows.h>
#endif
#ifdef FC_OS_MACOSX
#include <OpenGL/gl.h>
#else
#include <GL/gl.h>
#endif
#include <algorithm>
#include <cfloat>

#include <Inventor/actions/SoGLRenderAction.h>
#include <Inventor/bundles/SoMaterialBundle.h>
#include <Inventor/bundles/SoTextureCoordinateBundle.h>
#include <Inventor/elements/SoCoordinateElement.h>
#include <Inventor/elements/SoLazyElement.h>
#include <Inventor/misc/SoState.h>
#endif

#include "SoPolygon.h"


using namespace MeshGui;

SO_NODE_SOURCE(SoPolygon)

void SoPolygon::initClass()
{
    SO_NODE_INIT_CLASS(SoPolygon, SoShape, "Shape");
}

SoPolygon::SoPolygon()
{
    SO_NODE_CONSTRUCTOR(SoPolygon);

    SO_NODE_ADD_FIELD(startIndex, (0));
    SO_NODE_ADD_FIELD(numVertices, (0));
    SO_NODE_ADD_FIELD(highlight, (false));
    SO_NODE_ADD_FIELD(render, (true));
}

/**
 * Renders the polygon.
 */
void SoPolygon::GLRender(SoGLRenderAction* action)
{
    if (shouldGLRender(action) && render.getValue()) {
        SoState* state = action->getState();
        const SoCoordinateElement* coords = SoCoordinateElement::getInstance(state);
        if (!coords) {
            return;
        }
        const SbVec3f* points = coords->getArrayPtr3();
        if (!points) {
            return;
        }

        SoMaterialBundle mb(action);
        SoTextureCoordinateBundle tb(action, true, false);
        SoLazyElement::setLightModel(state, SoLazyElement::BASE_COLOR);
        mb.sendFirst();  // make sure we have the correct material

        int32_t len = coords->getNum();
        drawPolygon(points, len);
    }
}

/**
 * Renders the polygon.
 */
void SoPolygon::drawPolygon(const SbVec3f* points, int32_t len) const
{
    glLineWidth(3.0F);
    int32_t beg = startIndex.getValue();
    int32_t cnt = numVertices.getValue();
    int32_t end = beg + cnt;
    if (end > len) {
        return;  // wrong setup, too few points
    }
    // draw control mesh
    glBegin(GL_LINES);
    for (int32_t i = beg; i < end; ++i) {
        int32_t j = (i - beg + 1) % cnt + beg;
        glVertex3fv(points[i].getValue());
        glVertex3fv(points[j].getValue());
    }
    glEnd();
}

/**
 * Calculates picked point based on primitives generated by subclasses.
 */
void SoPolygon::rayPick(SoRayPickAction* action)
{
    // if (this->shouldRayPick(action)) {
    //     this->computeObjectSpaceRay(action);

    //    const SoBoundingBoxCache* bboxcache = getBoundingBoxCache();
    //    if (!bboxcache || !bboxcache->isValid(action->getState()) ||
    //        SoFCMeshObjectShape_ray_intersect(action, bboxcache->getProjectedBox())) {
    //        this->generatePrimitives(action);
    //    }
    //}
    inherited::rayPick(action);
}

void SoPolygon::generatePrimitives(SoAction* /*action*/)
{}

/**
 * Sets the bounding box of the mesh to \a box and its center to \a center.
 */
void SoPolygon::computeBBox(SoAction* action, SbBox3f& box, SbVec3f& center)
{
    SoState* state = action->getState();
    const SoCoordinateElement* coords = SoCoordinateElement::getInstance(state);
    if (!coords) {
        return;
    }
    const SbVec3f* points = coords->getArrayPtr3();
    if (!points) {
        return;
    }
    float maxX = -FLT_MAX, minX = FLT_MAX, maxY = -FLT_MAX, minY = FLT_MAX, maxZ = -FLT_MAX,
          minZ = FLT_MAX;
    int32_t len = coords->getNum();
    int32_t beg = startIndex.getValue();
    int32_t cnt = numVertices.getValue();
    int32_t end = beg + cnt;
    if (end <= len) {
        for (int32_t i = beg; i < end; i++) {
            maxX = std::max<float>(maxX, points[i][0]);
            minX = std::min<float>(minX, points[i][0]);
            maxY = std::max<float>(maxY, points[i][1]);
            minY = std::min<float>(minY, points[i][1]);
            maxZ = std::max<float>(maxZ, points[i][2]);
            minZ = std::min<float>(minZ, points[i][2]);
        }

        box.setBounds(minX, minY, minZ, maxX, maxY, maxZ);
        center.setValue(0.5F * (minX + maxX), 0.5F * (minY + maxY), 0.5F * (minZ + maxZ));
    }
    else {
        box.setBounds(SbVec3f(0, 0, 0), SbVec3f(0, 0, 0));
        center.setValue(0.0F, 0.0F, 0.0F);
    }
}
